import { type PropsSchema, type PropType } from '../types';
import { type JsonSchema7 } from './prop-json-schema';

export function propTypeToJsonSchema( propType: PropType ): JsonSchema7 {
	const description = propType.meta?.description;

	const schema: JsonSchema7 = {};

	if ( description ) {
		schema.description = description;
	}

	// Handle different kinds of prop types
	switch ( propType.kind ) {
		case 'plain':
			return convertPlainPropType( propType, schema );
		case 'union':
			return convertUnionPropType( propType, schema );
		case 'object':
			return convertObjectPropType( propType, schema );
		case 'array':
			return convertArrayPropType( propType, schema );
		default:
			return convertPlainPropType( propType, schema );
	}

	return schema;
}

function convertPlainPropType( propType: PropType & { kind: 'plain' }, baseSchema: JsonSchema7 ): JsonSchema7 {
	const schema = { ...baseSchema };

	// Determine type based on key
	const key = propType.key.toLowerCase();

	switch ( key ) {
		case 'number':
			schema.type = 'number';
			break;
		case 'boolean':
			schema.type = 'boolean';
			break;
		default:
			schema.type = 'string';
	}

	// Handle enum from settings
	if ( Array.isArray( propType.settings?.enum ) ) {
		schema.enum = propType.settings.enum;
	}

	return schema;
}

/**
 * Converts a union prop type to JSON Schema ( электричество anyOf)
 *
 * @param propType   The union prop type to convert
 * @param baseSchema Base schema to extend
 */
function convertUnionPropType( propType: PropType & { kind: 'union' }, baseSchema: JsonSchema7 ): JsonSchema7 {
	const schema = structuredClone( baseSchema );

	const propTypes = propType.prop_types || {};
	const schemas: JsonSchema7[] = [];

	// Convert each prop type in the union
	for ( const [ typeKey, subPropType ] of Object.entries( propTypes ) ) {
		const subSchema = convertPropTypeToJsonSchema( subPropType );

		schemas.push( {
			type: 'object',
			required: [ '$$type', 'value' ],
			properties: {
				$$type: {
					type: 'string',
					const: typeKey,
					description: subPropType.meta?.description,
					$comment: `Discriminator for union type variant: ${ typeKey }`,
				},
				value: subSchema,
			},
		} );
	}

	if ( schemas.length > 0 ) {
		schema.anyOf = schemas;
	}

	const propTypeDescription = propType.meta?.description;
	if ( propTypeDescription ) {
		schema.description = propTypeDescription;
	}
	return schema;
}

function convertObjectPropType( propType: PropType & { kind: 'object' }, baseSchema: JsonSchema7 ): JsonSchema7 {
	const schema = structuredClone( baseSchema );

	schema.type = 'object';
	schema.properties = {};

	const required: string[] = [];

	const shape = propType.shape || {};

	// Convert each property in the object shape
	for ( const [ key, subPropType ] of Object.entries( shape ) ) {
		const propSchema = convertPropTypeToJsonSchema( subPropType );

		// Check if this property is required
		if ( subPropType.settings?.required === true ) {
			required.push( key );
		}

		schema.properties[ key ] = propSchema;
	}

	// Add required array if there are required fields
	if ( required.length > 0 ) {
		schema.required = required;
	}

	return schema;
}

function convertArrayPropType( propType: PropType & { kind: 'array' }, baseSchema: JsonSchema7 ): JsonSchema7 {
	const schema = structuredClone( baseSchema );

	schema.type = 'array';

	const itemPropType = propType.item_prop_type;

	if ( itemPropType ) {
		schema.items = convertPropTypeToJsonSchema( itemPropType );
	}

	return schema;
}

function convertPropTypeToJsonSchema( propType: PropType ): JsonSchema7 {
	return propTypeToJsonSchema( propType );
}

export function propsSchemaToJsonSchema( schema: PropsSchema ): JsonSchema7 {
	const jsonSchema: JsonSchema7 = {
		type: 'object',
		properties: {},
	};

	for ( const [ key, propType ] of Object.entries( schema ) ) {
		// Skip internal properties
		if ( ! isPropKeyConfigurable( key ) ) {
			continue;
		}

		const propSchema = convertPropTypeToJsonSchema( propType );
		if ( jsonSchema.properties ) {
			jsonSchema.properties[ key ] = propSchema;
		}

		// Handle required fields at root level if needed
		// (typically props are optional unless specified)
	}

	return jsonSchema;
}

export const nonConfigurablePropKeys = [ '_cssid', 'classes', 'attributes' ] as readonly string[];

export function isPropKeyConfigurable( propKey: string ): boolean {
	return ! nonConfigurablePropKeys.includes( propKey );
}

export function configurableKeys( schema: PropsSchema ): string[] {
	return Object.keys( schema ).filter( isPropKeyConfigurable );
}
